﻿using FDStudio.Core.Config;
using FDStudio.Core.Container;
using FDStudio.Core.Controllers;
using FDStudio.Core.Entities;
using FDStudio.Core.Repositories;
using FDStudio.Core.Serializer;
using FDStudio.Core.Services;
using Microsoft.Extensions.DependencyModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace FDStudio.Core.Runtime
{
    /// <summary>
    /// 运行时上下文
    /// </summary>
    public class RuntimeContext
    {
        private static RuntimeContext _context;
        private AssemblyLoader _assemblyLoader;
        private IContainer _container;

        public IJsonSerializer JsonSerializer { get; set; }

        private RuntimeContext()
        {
            _assemblyLoader = new AssemblyLoader();
        }

        /// <summary>
        /// 当前上下文
        /// </summary>
        public static RuntimeContext Current { get { return _context; } }

        /// <summary>
        /// 运行时路径
        /// </summary>
        public RuntimePath RuntimePath { get; private set; }

        /// <summary>
        /// 当前容器
        /// </summary>
        public IContainer Container { get { return _container; } }

        private DependencyContext _dependencyContext = DependencyContext.Load(Assembly.GetEntryAssembly());

        /// <summary>
        /// 当前加载的程序集
        /// </summary>
        public List<Assembly> Assemblies { get; private set; } = new List<Assembly>();

        public List<Type> Types { get; set; } = new List<Type>();
        //public List<Type> Repositories { get; set; } = new List<Type>();
        //public List<Type> Controllers { get; set; } = new List<Type>();
        //public List<Type> Services { get; set; } = new List<Type>();
        //public List<Type> Models { get; set; } = new List<Type>();
        //public List<Type> CommonModels { get; set; } = new List<Type>();

        /// <summary>
        /// 配置项
        /// </summary>
        public AppConfig AppConfig { get; set; }

        static RuntimeContext()
        {
            _context = new RuntimeContext();
            _context.LoadAssembly();
        }

        /// <summary>
        /// 加载程序集
        /// </summary>
        private void LoadAssembly()
        {
            var path = new RuntimePath();
            Assemblies = _dependencyContext.RuntimeLibraries
                .SelectMany(lib => lib.GetDefaultAssemblyNames(_dependencyContext))
                .Where(an => an.Name.StartsWith("FD"))
                .Select(Assembly.Load).ToList();
            path.Root = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            path.Web = path.Root;
            _context.RuntimePath = path;

            var controllers = Directory.GetFiles(path.Controllers).Where(f => f.EndsWith(".dll"));
            ResolveAssemblies(controllers);
            var models = Directory.GetFiles(path.Models).Where(f => f.EndsWith(".dll"));
            ResolveAssemblies(models);
            var repositories = Directory.GetFiles(path.Repositories).Where(f => f.EndsWith(".dll"));
            ResolveAssemblies(repositories);
            var services = Directory.GetFiles(path.Services).Where(f => f.EndsWith(".dll"));
            ResolveAssemblies(services);
            var appServices = Directory.GetFiles(path.AppServices).Where(f => f.EndsWith(".dll"));
            ResolveAssemblies(appServices);

            Assemblies.ForEach(asm =>
            {
                var exTyeps = asm.GetExportedTypes();
                ResolveTypes(exTyeps);
                //try
                //{
                //}
                //catch (FileNotFoundException ex)
                //{
                //    //var fName = ex.FileName;
                //    //fName = fName.Substring(0, fName.IndexOf(','));
                //    //fName = Path.Combine(path.Root, $"{fName}.dll");
                //    //var a = _assemblyLoader.LoadFromAssemblyPath(fName);
                //    //var dd = a.ExportedTypes;
                //    TryLoadDependecies(ex, path);
                //    //var assembly = _assemblyLoader.LoadFromAssemblyPath(asm.Location); //Assembly.LoadFile(file);
                //    //var exTyeps = assembly.GetExportedTypes();
                //    var exTyeps = asm.GetExportedTypes();
                //    ResolveTypes(exTyeps);
                //}
            });

            // 特殊库
            LoadOther();
        }

        private void TryLoadDependecies(FileNotFoundException e, RuntimePath path)
        {
            try
            {
                var fName = e.FileName;
                fName = fName.Substring(0, fName.IndexOf(','));
                fName = Path.Combine(path.Root, $"{fName}.dll");
                var a = _assemblyLoader.LoadFromAssemblyPath(fName);
                var dd = a.ExportedTypes;
            }
            catch (FileNotFoundException ex)
            {
                TryLoadDependecies(ex, path);
            }
        }

        /// <summary>
        /// 解析文件到类型
        /// </summary>
        /// <param name="assemblies">程序集文件</param>
        private void ResolveAssemblies(IEnumerable<string> assemblies)
        {
            foreach (var file in assemblies)
            {
                if (Assemblies.Where(a => Path.GetFileName(a.Location) == Path.GetFileName(file)).Count() > 0)
                {
                    continue;
                }
                var assembly = _assemblyLoader.LoadFromAssemblyPath(file); //Assembly.LoadFile(file);
                var exTyeps = assembly.GetExportedTypes();

                ResolveTypes(exTyeps);
            }
        }

        /// <summary>
        /// 解析类型
        /// </summary>
        /// <param name="exTyeps"></param>
        private void ResolveTypes(IList<Type> exTyeps)
        {
            exTyeps.ToList().ForEach(t =>
            {
                AddType(t);
            });
        }

        /// <summary>
        /// 添加类型
        /// </summary>
        /// <param name="type"></param>
        private void AddType(Type type)
        {
            if (Types.Contains(type))
                return;
            Types.Add(type);
        }

        private void LoadOther()
        {
            //_assemblyLoader.Log(_dependencyContext);
            return;
            var otherAsms = new List<string>
            {
                "System.Data.Common"
            };
            otherAsms.ForEach(a =>
            {
                var asm = Assembly.Load(a);
                DependencyContext.Load(asm);
            });
        }

        /// <summary>
        /// 注册容器
        /// </summary>
        /// <param name="container"></param>
        internal void RegistContainer(IContainer container)
        {
            _container = container;
        }
    }
}
